package com.djl.zenithbite.service.shopping.order.impl;

import com.djl.zenithbite.common.response.BaseResponse;
import com.djl.zenithbite.common.threadlocal.UserContextHolder;
import com.djl.zenithbite.dto.request.shopping.order.AddOrderRequest;
import com.djl.zenithbite.dto.response.order.OrderDetailResponse;
import com.djl.zenithbite.dto.response.order.OrderItemResponse;
import com.djl.zenithbite.entity.*;
import com.djl.zenithbite.repository.*;
import com.djl.zenithbite.service.shopping.order.OrderService;
import com.djl.zenithbite.util.check.AssertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private UserReviewMapper userReviewMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private StoreMapper storeMapper;

    @Autowired
    private CartMapper cartMapper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResponse<Integer> addOrder(AddOrderRequest request) {
        //1. check request
        check(request);

        Store store = storeMapper.selectByPrimaryKey(request.getStoreId());
        request.setFee(store.getDeliveryFee());

        //2. generate order
        List<OrderItem> orderItemList = Optional.ofNullable(request.getOrderItemRequests()).orElse(new ArrayList<>()).stream()
                .map(orderItemRequest -> {

                    cartMapper.deleteByPrimaryKey(orderItemRequest.getCartId());

                    Product product = productMapper.selectByPrimaryKey(orderItemRequest.getProductId());

                    OrderItem orderItem = new OrderItem();
                    orderItem.setCreatedAt(new Date());
                    orderItem.setUpdatedAt(new Date());
                    orderItem.setQuantity(orderItemRequest.getOrderNum());
                    orderItem.setProductId(orderItemRequest.getProductId());
                    orderItem.setPrice(product.getProductPrice());
                    orderItem.setTotalPrice(product.getProductPrice().multiply(new BigDecimal(String.valueOf(orderItemRequest.getOrderNum()))));
                    return orderItem;
                })
                .collect(Collectors.toList());

        Order order = generateOrder(orderItemList, request);

        orderMapper.insertSelective(order);

        orderItemList.forEach(orderItem -> {
            orderItem.setOrderId(order.getOrderId());
        });

        for (OrderItem orderItem : orderItemList) {
            orderItemMapper.insertSelective(orderItem);
        }

        BaseResponse<Integer> response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);
        response.setData(order.getOrderId());

        return response;
    }

    @Override
    public BaseResponse<List<OrderDetailResponse>> listOrder() {
        Integer id = UserContextHolder.getUserPrimaryId();
        OrderExample example = new OrderExample();
        example.setOrderByClause("created_at desc");
        OrderExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(id);
        List<Order> orderList = orderMapper.selectByExample(example);

        List<OrderDetailResponse> orderDetailResponseList = Optional.ofNullable(orderList).orElse(new ArrayList<>())
                .stream().map(order -> {
                    OrderDetailResponse response = new OrderDetailResponse();
                    response.setStore(storeMapper.selectByPrimaryKey(order.getStoreId()));
                    response.setOrder(order);

                    UserReviewExample userReviewExample = new UserReviewExample();
                    UserReviewExample.Criteria criteria1 = userReviewExample.createCriteria();
                    criteria1.andOrderIdEqualTo(order.getOrderId());
                    int i = userReviewMapper.countByExample(userReviewExample);
                    response.setHasComment(i > 0);

                    OrderItemExample orderItemExample = new OrderItemExample();
                    OrderItemExample.Criteria orderItemExampleCriteria = orderItemExample.createCriteria();
                    orderItemExampleCriteria.andOrderIdEqualTo(order.getOrderId());
                    List<OrderItem> orderItems = orderItemMapper.selectByExample(orderItemExample);
                    List<OrderItemResponse> orderItemResponseList = Optional.ofNullable(orderItems).orElse(new ArrayList<>()).stream().map(orderItem -> {
                        OrderItemResponse orderItemResponse = buildResponse(orderItem);
                        return orderItemResponse;
                    }).collect(Collectors.toList());
                    response.setOrderItemList(orderItemResponseList);
                    return response;
                })
                .collect(Collectors.toList());
        BaseResponse<List<OrderDetailResponse>> response = new BaseResponse();
        response.setData(orderDetailResponseList);
        response.setCode(BaseResponse.SUCCESS);
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseResponse<Void> deleteOrder(Integer orderId) {

        AssertUtil.notNull(orderId,"order id can not be null!!!");

        OrderItemExample orderItemExample = new OrderItemExample();
        OrderItemExample.Criteria orderItemExampleCriteria = orderItemExample.createCriteria();
        orderItemExampleCriteria.andOrderIdEqualTo(orderId);

        orderItemMapper.deleteByExample(orderItemExample);

        OrderExample orderExample = new OrderExample();
        OrderExample.Criteria criteria = orderExample.createCriteria();
        criteria.andOrderIdEqualTo(orderId);

        orderMapper.deleteByExample(orderExample);

        BaseResponse<Void> response = new BaseResponse();
        response.setCode(BaseResponse.SUCCESS);

        return response;
    }

    private OrderItemResponse buildResponse(OrderItem orderItem){
        OrderItemResponse orderItemResponse = new OrderItemResponse();
        orderItemResponse.setProduct(productMapper.selectByPrimaryKey(orderItem.getProductId()));
        orderItemResponse.setOrderItemId(orderItem.getOrderItemId());
        orderItemResponse.setOrderId(orderItem.getOrderId());
        orderItemResponse.setProductId(orderItem.getProductId());
        orderItemResponse.setQuantity(orderItem.getQuantity());
        orderItemResponse.setPrice(orderItem.getPrice());
        orderItemResponse.setTotalPrice(orderItem.getTotalPrice());
        orderItemResponse.setCreatedAt(orderItem.getCreatedAt());
        orderItemResponse.setUpdatedAt(orderItem.getUpdatedAt());
        return orderItemResponse;

    }


    private Order generateOrder(List<OrderItem> orderItems,AddOrderRequest request){

        BigDecimal total = new BigDecimal("0");

        for (int i = 0;i< orderItems.size();i++){
            total = total.add(orderItems.get(i).getTotalPrice());
        }
        Order order = new Order();
        order.setTotalAmount(total);

        total = total.add(request.getFee());

        order.setUserId(UserContextHolder.getUserPrimaryId());
        order.setStoreId(request.getStoreId());
        order.setDeliveryFee(request.getFee());
        order.setDiscountAmount(new BigDecimal("0"));
        order.setFinalAmount(total);
        order.setPaymentMethod("alipay");
        order.setPaymentStatus(true);
        order.setOrderStatus(true);
        order.setDeliveryAddress(request.getAddressId());
        order.setCreatedAt(new Date());
        order.setUpdatedAt(new Date());
        return order;
    }

    private void check(AddOrderRequest request){
        AssertUtil.notNull(UserContextHolder.getUserPrimaryId(),"USER NOT LOGIN");
        AssertUtil.notNull(request,"request can not be null");
        AssertUtil.isNotBlank(request.getAddressId(),"addressId can not be null");
        AssertUtil.isTrue(!CollectionUtils.isEmpty(request.getOrderItemRequests()),"order item can not be null");
    }
}
